package org.ykhl.log.commons.sender;

import org.ykhl.log.commons.message.ErrorLogMessage;
import org.ykhl.log.commons.message.LogMessage;
import org.ykhl.log.commons.message.ReceiveSendLogMessage;
import org.ykhl.mq.commons.BaseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

/**
 * Created by zkf on 2015/12/10.
 */
public abstract class LogSender {
    private static final Logger LOG;
    private static final String SERVER_ADDRESS;

    static {
        LOG = LoggerFactory.getLogger(LogSender.class);
        try {
            String tmpAddress = null;
            String stupidAddress = null;
            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
            while(nis.hasMoreElements()){
                Enumeration<InetAddress> ias = nis.nextElement().getInetAddresses();
                while(ias.hasMoreElements()){
                    InetAddress ia = ias.nextElement();
                    String addr = ia.getHostAddress();
                    if(! ia.isLoopbackAddress() && !ia.isLinkLocalAddress() && ia.isSiteLocalAddress()) {
                        tmpAddress = addr;
                        LOG.info("found local ip address: " + tmpAddress);
                    }
                    else if (addr.startsWith("172.") || addr.startsWith("192.")){
                        // get those stupid ip addresses which look like local but really not.
                        stupidAddress = addr;
                        LOG.info("found stupid local ip address: " + stupidAddress);
                    }
                }
            }
            if(tmpAddress == null && stupidAddress == null) {
                LOG.error("can't get the ip address of this server");
                throw new RuntimeException("can't get the ip address of this server");
            }
            SERVER_ADDRESS = tmpAddress != null ? tmpAddress : stupidAddress;
            LOG.info("use local ip address: " + SERVER_ADDRESS);
        } catch (IOException e) {
            LOG.error("can't get the ip address of this server", e);
            throw new RuntimeException(e);
        }
    }

    protected String mqNameServerAddress;
    protected Boolean useSsl;

    public void setMqNameServerAddress(String mqNameServerAddress) {
        this.mqNameServerAddress = mqNameServerAddress;
    }

    public void setUseSsl(boolean useSsl) {
        this.useSsl = useSsl;
    }


    public void messageReceivedFromRequest(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.RECEIVE_FROM_REQUEST);
        messageReceivedOrSent(log, baseMessage.getAppServerId(),
                baseMessage.getRequestId() , baseMessage);
    }

    public void messageSentToMqHos(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.SEND_TO_MQHOS);
        messageReceivedOrSent(log, baseMessage.getRequestId(),
                baseMessage.getAppServerId(), baseMessage);
    }

    public void messageReceivedFromRestful(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.RECEIVE_FROM_RESTFUL);
        messageReceivedOrSent(log, baseMessage.getRequestId(),
                baseMessage.getAppServerId(), baseMessage);
    }
    public void messageSentToHospital(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.SEND_TO_HOSPITAL);
        messageReceivedOrSent(log, baseMessage.getAppServerId(),
                baseMessage.getHospitalId(), baseMessage);
    }
    public void messageReceivedFromHospital(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.RECEIVE_FROM_HOSPITAL);
        messageReceivedOrSent(log, baseMessage.getHospitalId(),
                baseMessage.getAppServerId(), baseMessage);
    }
    //发往进行转换
    public void messageSentToTransform(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.SEND_TO_TRANSFORM);
        messageReceivedOrSent(log, baseMessage.getAppServerId(),
                LogMessage.TransformId, baseMessage);
    }
    //收到转换后的消息
    public void messageReceivedFromTransform(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.RECEIVE_FROM_TRANSFORM);
        messageReceivedOrSent(log, LogMessage.TransformId,
                baseMessage.getAppServerId() , baseMessage);
    }
//消息返回到restful
    public void messageSentToRestful(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.SEND_TO_RESTFUL);
        messageReceivedOrSent(log, baseMessage.getAppServerId(),
                baseMessage.getRequestId(), baseMessage);
    }

    public void messageReceiveFromMqHos(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.RECEIVE_FROM_MQHOS);
        messageReceivedOrSent(log, baseMessage.getAppServerId(),
                baseMessage.getRequestId(), baseMessage);
    }

    public void messageSentToResponse(BaseMessage baseMessage) {
        ReceiveSendLogMessage log = new ReceiveSendLogMessage();
        log.setLogType(LogMessage.LogType.SEND_TO_RESPONSE);
        messageReceivedOrSent(log, baseMessage.getRequestId(),
                baseMessage.getAppServerId() , baseMessage);
    }


    private void messageReceivedOrSent(ReceiveSendLogMessage log, String from, String to,
                                       BaseMessage baseMessage) {
        fillBaseInfo(log);
        log.setFromAuthId(from);
        log.setToAuthId(to);
        log.setDataType(baseMessage.getDataType());
        log.setDataVersion(baseMessage.getDataVersion());
        log.setMessageMd5(baseMessage.getMessageBodyMd5());
        log.setBornTimestamp(Long.parseLong(baseMessage.getTimestamp()));
        log.setRequestId(baseMessage.getRequestId());
        sendLog(log);
    }

    public void serviceStarted() {
        LogMessage log = new LogMessage();
        fillBaseInfo(log);
        log.setLogType(LogMessage.LogType.STARTUP);
        sendLog(log);
    }

    public void serviceShutdown() {
        LogMessage log = new LogMessage();
        fillBaseInfo(log);
        log.setLogType(LogMessage.LogType.SHUTDOWN);
        sendLog(log);
    }

    public void error(String msg, Exception e) {
        ErrorLogMessage log = new ErrorLogMessage();
        fillBaseInfo(log);
        log.setLogType(LogMessage.LogType.ERROR);
        log.setErrorMessage(msg);
        StringWriter errors = new StringWriter();
        e.printStackTrace(new PrintWriter(errors));
        log.setStackTrace(errors.toString());
        sendLog(log);
    }

    public void error(Exception e) {
        error(null, e);
    }

    private void sendLog(LogMessage log){
        try {
            sendMessage(log.toJson());
        } catch (Exception e) {
            LOG.error("Failed to send log message", e);
        }
    }

    private void fillBaseInfo(LogMessage log){
        log.setServerIp(SERVER_ADDRESS);
        log.setTimestamp(System.currentTimeMillis());
    }

    public abstract void init();
    public abstract void destroy();
    public abstract void flush();
    protected abstract void sendMessage(String jsonLogMessage) throws Exception;
}
